package com.quectel.core.module.parking.service.impl;


import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.base.Preconditions;
import com.quectel.constant.core.parking.ParkingChargingConstants;
import com.quectel.constant.core.parking.ParkingOrderConstants;
import com.quectel.constant.global.SqlConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.util.aspect.lock.annotation.Lock;
import com.quectel.core.constants.RedisCacheConstants;
import com.quectel.core.constants.RedisLockConstants;
import com.quectel.core.module.car.dto.CarDto;
import com.quectel.core.module.car.service.CarService;
import com.quectel.core.module.carparking.dto.CarParkingDoorDto;
import com.quectel.core.module.carparking.service.CarParkingDoorService;
import com.quectel.core.module.carparking.service.CarParkingService;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.household.dto.HouseholdDto;
import com.quectel.core.module.household.service.HouseholdService;
import com.quectel.core.module.parking.dao.ParkingOrderDao;
import com.quectel.core.module.parking.dto.*;
import com.quectel.core.module.parking.entity.ParkingOrderEntity;
import com.quectel.core.module.parking.service.ParkingChargingService;
import com.quectel.core.module.parking.service.ParkingCurrentCarService;
import com.quectel.core.module.parking.service.ParkingOrderService;
import com.quectel.core.module.parkingcard.dto.ParkingCardMemberDto;
import com.quectel.core.module.parkingcard.service.ParkingCardMemberService;
import com.quectel.core.module.village.dto.VillageDto;
import com.quectel.core.module.village.service.VillageService;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.ex.CheckErrorException;
import com.quectel.util.kit.CacheKit;
import com.quectel.util.kit.Snowflake;
import com.quectel.util.mybatis.MyBatisPlusUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author ryan
 * @email ryan.ran@quectel.com
 * @date 2023/04/13 11:16
 */
@DubboService
public class ParkingOrderServiceImpl implements ParkingOrderService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ParkingOrderService.class);

    /**
     * 入参解释:实体id
     */
    private static final Function<Long, String> PARKING_ORDER_CACHE_BY_ID_FUNC = id -> String.format(RedisCacheConstants.PROJECT_NAME + "PARKING_ORDER_CACHE_BY_ID:%d", id);

    /**
     * 清除缓存方法
     */
    private static final Consumer<ParkingOrderDto> CLEAR_CACHE_FUNC = dto -> {
        CacheKit.invalidRedisCache(PARKING_ORDER_CACHE_BY_ID_FUNC.apply(dto.getId()));
    };

    @Autowired
    private ParkingOrderDao parkingOrderDao;
    @Autowired
    private CarParkingService carParkingService;
    @Autowired
    private CarParkingDoorService carParkingDoorService;
    @Autowired
    private ParkingCurrentCarService parkingCurrentCarService;
    @Autowired
    private VillageService villageService;
    @Autowired
    private ParkingChargingService parkingChargingService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private CarService carService;
    @Autowired
    private HouseholdService householdService;
    @Autowired
    private ParkingCardMemberService parkingCardMemberService;

    @Override
    public List<ParkingOrderDto> queryTreeList(Map<String, Object> params) {
        List<ParkingOrderDto> allList = Lists.newArrayList();

        List<ParkingOrderEntity> parkingOrderEntities = parkingOrderDao.queryTreeRootList(params);
        List<ParkingOrderDto> list = CopyUtils.copyList(parkingOrderEntities, ParkingOrderDto.class);

        for (ParkingOrderDto parkingOrderDto : list) {
            ParkingOrderDto childDto = selectByPreId(parkingOrderDto.getId());
            if (Objects.nonNull(childDto)) {
                allList.add(childDto);
            }
        }

        allList.addAll(list);
        allList.forEach(this::paddingField);
        return allList;
    }

    @Override
    public int queryTreeTotal(Map<String, Object> params) {
        return parkingOrderDao.queryTreeTotal(params);
    }

    @Override
    public List<CountOrderByTimeDto> countOrderGroupByTime(Map<String, Object> params) {
        if (params.get(SqlConstants.DATE_FORMAT_TYPE_FILED) == null) {
            throw new CheckErrorException("时间格式化类型为必传参数");
        }
        return parkingOrderDao.countRecordGroupByTime(params);
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.PARKING_ORDER_LOCK + "#{#crimpingReq.license}_#{#crimpingReq.licenseColorEnum}"
            }
    )
    @Transactional
    public CrimpingCheckRespDto crimping(CrimpingReqDto crimpingReq) {
        final Date currentDate = new Date();
        CrimpingCheckRespDto resp = new CrimpingCheckRespDto();
        resp.setAllowOpen(Boolean.FALSE);

        DeviceDto device = deviceService.selectCacheById(crimpingReq.getDeviceId());
        Preconditions.checkNotNull(device);

        ParkingCurrentCarDto parkingCurrentCarDto = parkingCurrentCarService.selectOneCar(device.getVillageId(), crimpingReq.getLicense(), crimpingReq.getLicenseColorEnum().getLicenseColor());
        if (Objects.isNull(parkingCurrentCarDto)) {
            // 未找到当前停车记录，说明该车入场时没被捕捉到，或者，压线检测车牌错误
            LOGGER.warn("停车压线检测 未知车辆 车牌 {} 车牌颜色类型 {}", crimpingReq, crimpingReq.getLicenseColorEnum().getLicenseColorName());
            return resp;
        }

        ParkingCardMemberDto memberDto = parkingCardMemberService.selectValidMember(device.getVillageId()
                , crimpingReq.getLicense(), crimpingReq.getLicenseColorEnum().getLicenseColor());

        ParkingOrderDto parkingOrderDto = selectById(parkingCurrentCarDto.getOrderId());
        Preconditions.checkNotNull(parkingOrderDto);
        resp.setOrderId(parkingOrderDto.getId());

        updateOrderFunc(parkingOrderDto, memberDto, device, crimpingReq);
        ParkingOrderDto overtimeOrder = selectByPreId(parkingOrderDto.getId());
        if (Objects.nonNull(overtimeOrder)) {
            // 存在超时订单
            updateOrderFunc(overtimeOrder, memberDto, device, crimpingReq);
        }

        if (Objects.nonNull(memberDto)) {
            // 是会员直接开
            resp.setAllowOpen(Boolean.TRUE);
            return resp;
        }

        if (ParkingOrderConstants.OrderPayStatusEnum.NO_PAY.getPayStatus().equals(parkingOrderDto.getPayStatus())) {
            return resp;
        }

        ParkingChargingDto parkingChargingDto = parkingChargingService.selectById(parkingOrderDto.getChargingConfId());
        Preconditions.checkNotNull(parkingChargingDto);

        if (ParkingOrderConstants.AdvanceEnum.PAY_ADVANCE.getAdvanceType().equals(parkingOrderDto.getIsAdvance())
                && DateUtil.between(parkingOrderDto.getPayTime(), crimpingReq.getCrimpingTime(), DateUnit.MINUTE, Boolean.FALSE) > parkingChargingDto.getPaymentExitTime()) {
            // 已支付状态 预支付 超时未出 超时订单未产生的情况下生成超时订单

            if (Objects.isNull(overtimeOrder)) {
                // 产生超时账单
                ParkingOrderDto newlyDto = new ParkingOrderDto();
                newlyDto.setTenantId(parkingOrderDto.getTenantId());
                newlyDto.setVillageId(parkingOrderDto.getVillageId());
                newlyDto.setHouseholdId(parkingOrderDto.getHouseholdId());
                newlyDto.setPreParkingOrderId(parkingOrderDto.getId());
                newlyDto.setCarId(parkingOrderDto.getCarId());
                newlyDto.setOwnerName(parkingOrderDto.getOwnerName());
                newlyDto.setOwnerMobile(parkingOrderDto.getOwnerMobile());
                newlyDto.setIsAdvance(ParkingOrderConstants.AdvanceEnum.NO_ADVANCE.getAdvanceType());
                newlyDto.setLicense(parkingOrderDto.getLicense());
                newlyDto.setLicenseColor(parkingOrderDto.getLicenseColor());
                newlyDto.setInDoorId(parkingOrderDto.getInDoorId());
                newlyDto.setInDeviceId(parkingOrderDto.getInDeviceId());
                newlyDto.setInTime(parkingOrderDto.getPayTime());
                newlyDto.setIsPayCard(parkingOrderDto.getIsPayCard());
                newlyDto.setInPictures(parkingOrderDto.getInPictures());
                newlyDto.setPayStatus(ParkingOrderConstants.OrderPayStatusEnum.NO_PAY.getPayStatus());
                newlyDto.setStatus(ParkingOrderConstants.OrderStatusEnum.IS_PROGRESS.getStatus());
                newlyDto.setInvoiceStatus(ParkingOrderConstants.OrderInvoiceStatusEnum.NOT_INVOICED.getInvoiceStatus());
                newlyDto.setInPictures(parkingOrderDto.getInPictures());
                newlyDto.setChargingConfId(parkingOrderDto.getChargingConfId());
                newlyDto.setChargingConfBak(parkingOrderDto.getChargingConfBak());
                newlyDto.setCrimpingDeviceId(device.getId());
                newlyDto.setCrimpingTime(crimpingReq.getCrimpingTime());
                newlyDto.setOpenGuardStatus(ParkingOrderConstants.GuardOpenStatusEnum.NEVER_OPENED.getOpenStatus());
                newlyDto.setCreateTime(currentDate);
                save(newlyDto);
            }

            return resp;
        }

        // 已支付预支付未超时 或 已支付非预支付订单开闸放行
        resp.setAllowOpen(Boolean.TRUE);
        return resp;
    }

    /**
     * 压线时,订单状态更新
     */
    private void updateOrderFunc(ParkingOrderDto parkingOrder, ParkingCardMemberDto validMember, DeviceDto device, CrimpingReqDto crimpingReq) {
        final Date currentDate = new Date();

        ParkingOrderDto updateDto = new ParkingOrderDto();
        updateDto.setId(parkingOrder.getId());
        updateDto.setCrimpingTime(crimpingReq.getCrimpingTime());
        updateDto.setCrimpingDeviceId(device.getId());

        if (ParkingOrderConstants.OrderPayStatusEnum.NO_PAY.getPayStatus().equals(parkingOrder.getPayStatus())) {
            // 未支付订单，才更改的一些订单状态
            updateDto.setIsPayCard(Objects.nonNull(validMember)
                    ? ParkingOrderConstants.PayCardEnum.PAY_CARD.getIsPayCard()
                    : ParkingOrderConstants.PayCardEnum.NO_CARD.getIsPayCard());

            if (Objects.nonNull(validMember)) {
                // 是会员完成订单
                updateDto.setDueAmount(crimpingReq.getDueAmount());
                updateDto.setMonthCardDiscount(crimpingReq.getDueAmount());
                updateDto.setPayType(ParkingOrderConstants.PayTypeEnum.CARD_PAY.getPayType());
                updateDto.setPayStatus(ParkingOrderConstants.OrderPayStatusEnum.PAY_SUCCESS.getPayStatus());
                updateDto.setPayAmount(BigDecimal.ZERO);
                updateDto.setPayTime(currentDate);

                // 会员节省的金额累加
                ParkingCardMemberDto newlyMemberDto = new ParkingCardMemberDto();
                newlyMemberDto.setId(validMember.getId());
                newlyMemberDto.setCumulativeSavings(validMember.getCumulativeSavings().add(crimpingReq.getDueAmount()));
                parkingCardMemberService.updateById(newlyMemberDto);
            }
        }
        updateById(updateDto);
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.PARKING_ORDER_LOCK + "#{#cleanReq.license}_#{#cleanReq.licenseColorEnum}"
            }
    )
    @Transactional
    public void clean(CleanReqDto cleanReq) {
        DeviceDto device = deviceService.selectCacheById(cleanReq.getDeviceId());
        Preconditions.checkNotNull(device);

        ParkingCurrentCarDto parkingCurrentCarDto = parkingCurrentCarService.selectOneCar(device.getVillageId(), cleanReq.getLicense(), cleanReq.getLicenseColorEnum().getLicenseColor());
        if (Objects.nonNull(parkingCurrentCarDto)) {
            parkingCurrentCarService.deleteById(parkingCurrentCarDto.getId());
            if (Objects.nonNull(parkingCurrentCarDto.getOrderId())) {
                CarParkingDoorDto carParkingDoorDto = carParkingDoorService.selectOne(device.getId());

                ParkingOrderDto newlyDto = new ParkingOrderDto();
                newlyDto.setId(parkingCurrentCarDto.getOrderId());
                newlyDto.setOutDoorId(Objects.nonNull(carParkingDoorDto) ? carParkingDoorDto.getId() : null);
                newlyDto.setOutDeviceId(device.getId());
                newlyDto.setOutTime(cleanReq.getOutTime());
                newlyDto.setOutPictures(cleanReq.getOutPictures());
                newlyDto.setStatus(ParkingOrderConstants.OrderStatusEnum.FINISHED.getStatus());
                updateById(newlyDto);

                ParkingOrderDto overtimeOrder = selectByPreId(parkingCurrentCarDto.getOrderId());
                if (Objects.nonNull(overtimeOrder)) {
                    // 存在超时订单
                    ParkingOrderDto newlyOvertimeDto = new ParkingOrderDto();
                    newlyOvertimeDto.setId(overtimeOrder.getId());
                    newlyOvertimeDto.setOutDoorId(Objects.nonNull(carParkingDoorDto) ? carParkingDoorDto.getId() : null);
                    newlyOvertimeDto.setOutDeviceId(device.getId());
                    newlyOvertimeDto.setOutTime(cleanReq.getOutTime());
                    newlyOvertimeDto.setOutPictures(cleanReq.getOutPictures());
                    newlyOvertimeDto.setStatus(ParkingOrderConstants.OrderStatusEnum.FINISHED.getStatus());
                    updateById(newlyOvertimeDto);
                }
            }
        }
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.PARKING_ORDER_LOCK + "#{#parkingReq.license}_#{#parkingReq.licenseColorEnum}"
            }
    )
    @Transactional
    public void parkReq(ParkingReqDto parkingReq) {
        DeviceDto deviceDto = deviceService.selectCacheById(parkingReq.getDeviceId());
        Preconditions.checkNotNull(deviceDto);

        ParkingCurrentCarDto oldDto = parkingCurrentCarService.selectOneCar(deviceDto.getVillageId(), parkingReq.getLicense(), parkingReq.getLicenseColorEnum().getLicenseColor());
        if (Objects.nonNull(oldDto)) {
            // 正常情况下，不会有旧停车记录
            if (Objects.nonNull(oldDto.getOrderId())) {
                ParkingOrderDto newlyDto = new ParkingOrderDto();
                newlyDto.setId(oldDto.getOrderId());
                newlyDto.setStatus(ParkingOrderConstants.OrderStatusEnum.ABNORMAL.getStatus());
                updateById(newlyDto);

                ParkingOrderDto overtimeOrder = selectByPreId(oldDto.getOrderId());
                if (Objects.nonNull(overtimeOrder)) {
                    ParkingOrderDto newlyOvertimeDto = new ParkingOrderDto();
                    newlyOvertimeDto.setId(overtimeOrder.getId());
                    newlyOvertimeDto.setStatus(ParkingOrderConstants.OrderStatusEnum.ABNORMAL.getStatus());
                    updateById(newlyOvertimeDto);
                }
            }

            parkingCurrentCarService.deleteById(oldDto.getId());
            LOGGER.warn("车辆异常入场 存在当前在停记录 license {}, licenseColor {} 已自动清理", oldDto.getLicense(), oldDto.getLicenseColor());
        }

        CarDto carDto = carService.selectOneByCache(deviceDto.getVillageId(), parkingReq.getLicense(), parkingReq.getLicenseColorEnum().getLicenseColor());
        HouseholdDto householdDto = null;
        if (Objects.nonNull(carDto)) {
            householdDto = householdService.selectByVillageIdAndMobile(deviceDto.getVillageId(), carDto.getOwnerMobile());
        }

        ParkingChargingDto parkingChargingDto = parkingChargingService.selectOne(deviceDto.getVillageId(), parkingReq.getCarType());

        ParkingCardMemberDto memberDto = parkingCardMemberService.selectValidMember(deviceDto.getVillageId()
                , parkingReq.getLicense(), parkingReq.getLicenseColorEnum().getLicenseColor());

        Long orderId = null;
        if (Boolean.TRUE.equals(parkingReq.getGenOrder())
                && Objects.nonNull(parkingChargingDto)
                && ParkingChargingConstants.StatusEnum.ENABLE.getStatus().equals(parkingChargingDto.getStatus())) {
            // 需要生成停车账单 并且 收费配置不为空 产生停车账单

            CarParkingDoorDto carParkingDoorDto = carParkingDoorService.selectOne(deviceDto.getId());

            // 产生停车账单
            ParkingOrderDto parkingOrderDto = new ParkingOrderDto();
            parkingOrderDto.setTenantId(deviceDto.getTenantId());
            parkingOrderDto.setVillageId(deviceDto.getVillageId());
            parkingOrderDto.setIsAdvance(ParkingOrderConstants.AdvanceEnum.NO_ADVANCE.getAdvanceType());
            parkingOrderDto.setHouseholdId(Objects.nonNull(householdDto) ? householdDto.getId() : null);
            parkingOrderDto.setCarId(Objects.nonNull(carDto) ? carDto.getId() : null);
            parkingOrderDto.setOwnerName(Objects.nonNull(carDto) ? carDto.getOwnerName() : null);
            parkingOrderDto.setOwnerMobile(Objects.nonNull(carDto) ? carDto.getOwnerMobile() : null);
            parkingOrderDto.setLicense(parkingReq.getLicense());
            parkingOrderDto.setLicenseColor(parkingReq.getLicenseColorEnum().getLicenseColor());
            parkingOrderDto.setInDoorId(Objects.nonNull(carParkingDoorDto) ? carParkingDoorDto.getId() : null);
            parkingOrderDto.setInDeviceId(deviceDto.getId());
            parkingOrderDto.setInTime(parkingReq.getInTime());
            parkingOrderDto.setInPictures(parkingReq.getInPictures());
            parkingOrderDto.setIsPayCard(Objects.nonNull(memberDto)
                    ? ParkingOrderConstants.PayCardEnum.PAY_CARD.getIsPayCard()
                    : ParkingOrderConstants.PayCardEnum.NO_CARD.getIsPayCard());
            parkingOrderDto.setPayStatus(ParkingOrderConstants.OrderPayStatusEnum.NO_PAY.getPayStatus());
            parkingOrderDto.setStatus(ParkingOrderConstants.OrderStatusEnum.IS_PROGRESS.getStatus());
            parkingOrderDto.setInvoiceStatus(ParkingOrderConstants.OrderInvoiceStatusEnum.NOT_INVOICED.getInvoiceStatus());
            parkingOrderDto.setChargingConfId(parkingChargingDto.getId());
            parkingOrderDto.setChargingConfBak(JacksonUtils.toJsonString(parkingChargingDto));
            parkingOrderDto.setOpenGuardStatus(ParkingOrderConstants.GuardOpenStatusEnum.NEVER_OPENED.getOpenStatus());
            parkingOrderDto.setCreateTime(new Date());
            orderId = save(parkingOrderDto);
        }

        ParkingCurrentCarDto parkingCurrentCarDto = new ParkingCurrentCarDto();
        parkingCurrentCarDto.setTenantId(deviceDto.getTenantId());
        parkingCurrentCarDto.setVillageId(deviceDto.getVillageId());
        parkingCurrentCarDto.setOrderId(orderId);
        parkingCurrentCarDto.setDeviceId(deviceDto.getId());
        parkingCurrentCarDto.setCarId(Objects.nonNull(carDto) ? carDto.getId() : null);
        parkingCurrentCarDto.setIsPayCard(Objects.nonNull(memberDto)
                ? ParkingOrderConstants.PayCardEnum.PAY_CARD.getIsPayCard()
                : ParkingOrderConstants.PayCardEnum.NO_CARD.getIsPayCard());
        parkingCurrentCarDto.setLicense(parkingReq.getLicense());
        parkingCurrentCarDto.setLicenseColor(parkingReq.getLicenseColorEnum().getLicenseColor());
        parkingCurrentCarDto.setOwnerMobile(Objects.nonNull(householdDto) ? householdDto.getMobile() : null);
        parkingCurrentCarDto.setOwnerName(Objects.nonNull(householdDto) ? householdDto.getName() : null);
        parkingCurrentCarDto.setInTime(parkingReq.getInTime());
        parkingCurrentCarService.save(parkingCurrentCarDto);
    }

    @Override
    public ParkingOrderDto selectByPreId(Long parkingOrderId) {
        ParkingOrderEntity parkingOrderEntity = parkingOrderDao.selectOne(new LambdaQueryWrapper<ParkingOrderEntity>()
                .eq(ParkingOrderEntity::getPreParkingOrderId, parkingOrderId)
                .last(SqlConstants.MYSQL_LIMIT_ONE));
        ParkingOrderDto parkingOrderDto = CopyUtils.copyObj(parkingOrderEntity, ParkingOrderDto.class);
        return paddingField(parkingOrderDto);
    }

    @Override
    public ParkingOrderDto selectCacheById(Long id) {
        return CacheKit.cacheToRedis(
                () -> selectById(id),
                PARKING_ORDER_CACHE_BY_ID_FUNC.apply(id),
                SystemConstants.NOT_NULL_CACHE_EXPIRE_SECONDS
        );
    }

    @Override
    public ParkingOrderDto selectById(Long id) {
        ParkingOrderEntity entity = parkingOrderDao.selectById(id);
        return paddingField(CopyUtils.copyObj(entity, ParkingOrderDto.class));
    }

    @Override
    public List<ParkingOrderDto> queryList(Map<String, Object> params) {
        List<ParkingOrderEntity> list = parkingOrderDao.queryList(params);
        List<ParkingOrderDto> result = CopyUtils.copyList(list, ParkingOrderDto.class);
        result.forEach(this::paddingField);
        return result;
    }

    /**
     * 此方法慎用 禁止填充List一类 比如一个人有多个地址这里不允许填充
     * 填充的原则是: 1:被填充对象不经常变换 2:根据id可以走缓存 3数据足够简单 比如设备类型 所属园区等
     *
     * @param dto
     * @return
     */
    private ParkingOrderDto paddingField(ParkingOrderDto dto) {
        if (dto != null) {
            VillageDto villageDto = villageService.selectCacheById(dto.getVillageId());
            dto.setVillage(villageDto);
            if (Objects.nonNull(villageDto)) {
                dto.setParkName(villageDto.getName() + "停车场");
            }
            dto.setInDoor(carParkingDoorService.selectCacheById(dto.getInDoorId()));
            dto.setOutDoor(carParkingDoorService.selectCacheById(dto.getOutDoorId()));
            dto.setCar(carService.selectCacheById(dto.getCarId()));

            Date endDate = Objects.isNull(dto.getOutTime()) ? new Date() : dto.getOutTime();
            long durationMinutes = DateUtil.between(dto.getInTime(), endDate, DateUnit.MINUTE, Boolean.TRUE);
            dto.setStayDuration(String.format("%s小时%s分钟", durationMinutes / 60, durationMinutes % 60));

            if (StringUtils.isNotBlank(dto.getChargingConfBak())) {
                dto.setParkingCharging(JacksonUtils.parse(dto.getChargingConfBak(), ParkingChargingDto.class));
            }

        }
        return dto;
    }

    @Override
    public int queryTotal(Map<String, Object> params) {
        return parkingOrderDao.queryTotal(params);
    }

    @Override
    public Long save(ParkingOrderDto dto) {
        ParkingOrderEntity entity = CopyUtils.copyObj(dto, ParkingOrderEntity.class);
        entity.setId(Snowflake.nextId());
        parkingOrderDao.insert(entity);
        return entity.getId();
    }

    @Override
    public void updateById(ParkingOrderDto dto) {
        ParkingOrderEntity entity = CopyUtils.copyObj(dto, ParkingOrderEntity.class);

        parkingOrderDao.updateById(entity);

        CLEAR_CACHE_FUNC.accept(dto);
    }

    @Override
    public void updateAllColumnById(ParkingOrderDto dto) {
        ParkingOrderEntity entity = CopyUtils.copyObj(dto, ParkingOrderEntity.class);

        MyBatisPlusUtils.updateAllColumnById(entity, parkingOrderDao);

        CLEAR_CACHE_FUNC.accept(dto);
    }

    @Override
    public void deleteById(Long id) {

        CLEAR_CACHE_FUNC.accept(selectById(id));

        parkingOrderDao.deleteById(id);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(Long[] ids) {
        for (Long id : ids) {
            deleteById(id);
        }
    }
}
